(defmacro defadvice! (symbol arglist &rest body)
  "Define an advice called SYMBOL and add it to PLACES.

ARGLIST is as in `defun'. WHERE is a keyword as passed to `advice-add', and
PLACE is the function to which to add the advice, like in `advice-add'.
DOCSTRING and BODY are as in `defun'.

\(fn SYMBOL ARGLIST &rest [WHERE PLACES...] BODY\)"
  (declare (indent defun))
  (let (where-alist)
    (while (keywordp (car body))
      (push `(cons ,(pop body) (ensure-list ,(pop body)))
            where-alist))
    `(progn
       (defun ,symbol ,arglist ,@body)
       (dolist (targets (list ,@(nreverse where-alist)))
         (dolist (target (cdr targets))
           (advice-add target (car targets) #',symbol))))))



;; for dired-mode
(when (eq system-type 'windows-nt)
  (setq default-directory "e:/workspace/"))
(when (not (eq system-type 'windows-nt))
  (setq default-directory "~/"))

;; default env for rc/require
(setq-default is-graphics (display-graphic-p))

;(eval-when-compile
;  (require 'use-package))
;(straight-use-package 'use-package)
(setq use-package-always-defer t)


(rc/require 'all-the-icons)

;; theme
;;     (rc/require 'modus-themes)
;;     (load-theme 'modus-operandi-tinted t)
(rc/require 'gruber-darker-theme)
(load-theme 'gruber-darker t)

(require 'init-nerd-font)

(rc/require 'doom-modeline)
(use-package doom-modeline
  :init
  (setq doom-modeline-icon t)
  (setq doom-modeline-major-mode-icon t)
  (setq doom-modeline-major-mode-color-icon t)
  (setq doom-modeline-buffer-state-icon t)
  (setq doom-modeline-modal-icon t)
  (setq doom-modeline-modal-modern-icon t)
  (doom-modeline-mode 1)
)
(rc/require 'restart-emacs)
(use-package restart-emacs
:defer .1
)

(rc/require 'sublimity)
(use-package sublimity
  :after (prog-mode)
  :config
  (sublimity-mode)
)

(rc/require 'centaur-tabs)
(use-package centaur-tabs
  :defer .1
  :config
  (setq centaur-tabs-enable-key-bindings t)
  (setq centaur-tabs-cycle-scope 'tabs)
  ;;(centaur-tabs-group-by-projectile-project)
  (centaur-tabs-mode)
  (defun centaur-tabs-buffer-groups ()
    "`centaur-tabs-buffer-groups' control buffers' group rules.

Group centaur-tabs with mode if buffer is derived from `eshell-mode' `emacs-lisp-mode' `dired-mode' `org-mode' `magit-mode'.
All buffer name start with * will group to \"Emacs\".
Other buffer group by `centaur-tabs-get-group-name' with project name."
    (list
     (cond
      ;; ((not (eq (file-remote-p (buffer-file-name)) nil))
      ;; "Remote")
      ((or (string-equal "*" (substring (buffer-name) 0 1))
           (memq major-mode '(magit-process-mode
                              magit-status-mode
                              magit-diff-mode
                              magit-log-mode
                              magit-file-mode
                              magit-blob-mode
                              magit-blame-mode
                              )))
       "Emacs")
      ((derived-mode-p 'prog-mode)
       "Editing")
      ((derived-mode-p 'dired-mode)
       "Dired")
      ((memq major-mode '(helpful-mode
                          help-mode))
       "Help")
      ((memq major-mode '(org-mode
                          org-agenda-clockreport-mode
                          org-src-mode
                          org-agenda-mode
                          org-beamer-mode
                          org-indent-mode
                          org-bullets-mode
                          org-cdlatex-mode
                          org-agenda-log-mode
                          diary-mode))
       "OrgMode")
      (t
       (centaur-tabs-get-group-name (current-buffer))))))
  :hook
  (dashboard-mode . centaur-tabs-local-mode)
  (term-mode . centaur-tabs-local-mode)
  :bind
  ("C-<prior>" . centaur-tabs-backward)
  ("C-<next>" . centaur-tabs-forward)
  (:map evil-normal-state-map
        ("g t" . centaur-tabs-forward)
        ("g T" . centaur-tabs-backward)))

;; intend bar
(rc/require 'indent-bars)
(use-package indent-bars
  :custom
  (indent-bars-no-descend-lists t) ; no extra bars in continued func arg lists
  (indent-bars-treesit-support t)
  (indent-bars-treesit-ignore-blank-lines-types '("module"))
  ;; Add other languages as needed
  (indent-bars-treesit-scope '((python function_definition class_definition for_statement
	  if_statement with_statement while_statement)))
  :hook (prog-mode . indent-bars-mode))



;; dashboard
(rc/require 'dashboard)
(use-package dashboard
  :init
  (setq dashboard-banner-logo-title "Ciallo～(∠・ω＜)⌒☆") ;; 个性签名，随读者喜好设置
  (setq dashboard-projects-backend 'projectile) ;; 读者可以暂时注释掉这一行，等安装了 projectile 后再使用
  (setq dashboard-startup-banner "~/.emacs.d/marivector.png") ;; https://gitee.com/asphodelus_dev/dotfile/blob/main/marivector.png
  (setq dashboard-items '((recents  . 5)   ;; 显示多少个最近文件
			  (bookmarks . 5)  ;; 显示多少个最近书签
			  (projects . 10))) ;; 显示多少个最近项目
  (setq dashboard-display-icons-p t)     ; display icons on both GUI and terminal
  (setq dashboard-icon-type 'nerd-icons) ; use `nerd-icons' package
  (setq dashboard-set-heading-icons t)
  (setq dashboard-set-file-icons t)
  (setq dashboard-projects-switch-function 'projectile-persp-switch-project)
  (dashboard-setup-startup-hook)
)

;; vertico-orderless-consult
(rc/require 'vertico 'orderless 'consult)
(use-package vertico
  :hook (vertico-posframe-mode . vertico-mode)
  :custom
  ;; (vertico-scroll-margin 0) ;; Different scroll margin
  ;; (vertico-count 20) ;; Show more candidates
  ;; (vertico-resize t) ;; Grow and shrink the Vertico minibuffer
  (vertico-cycle t) ;; Enable cycling for `vertico-next/previous'
)

;; Configure directory extension.
(use-package vertico-directory
  :after vertico
  :ensure nil
  ;; More convenient directory navigation commands
  :bind (:map vertico-map
              ("RET" . vertico-directory-enter)
              ("DEL" . vertico-directory-delete-word))
  ;; Tidy shadowed file names
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

(rc/require 'vertico-posframe)
(use-package vertico-posframe
  :defer .1
  :config (vertico-posframe-mode))

;; Persist history over Emacs restarts. Vertico sorts by history position.
(rc/require 'savehist)
(use-package savehist
  :hook (vertico-mode . savehist-mode)
)

;; A few more useful configurations...
(use-package emacs
  :custom
  ;; Support opening new minibuffers from inside existing minibuffers.
  (enable-recursive-minibuffers t)
  ;; Hide commands in M-x which do not work in the current mode.  Vertico
  ;; commands are hidden in normal buffers. This setting is useful beyond
  ;; Vertico.
  (read-extended-command-predicate #'command-completion-default-include-p)
  :init
  ;; Add prompt indicator to `completing-read-multiple'.
  ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
  (defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                  (replace-regexp-in-string
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                   crm-separator)
                  (car args))
          (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode))
(use-package orderless
  :defer .1
  :custom
  ;; Configure a custom style dispatcher (see the Consult wiki)
  ;; (orderless-style-dispatchers '(+orderless-consult-dispatch orderless-affix-dispatch))
  ;; (orderless-component-separator #'orderless-escapable-split-on-space)
  (completion-styles '(orderless basic partial-completion))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles partial-completion))))
  :config
  (setq read-file-name-completion-ignore-case t
      read-buffer-completion-ignore-case t
      completion-ignore-case t)
)
(use-package consult
:bind(
;; A recursive grep
  ("C-c r" . consult-ripgrep)
  ;; search files and create new files
  ("C-x f" . consult-find)
  ;; Search through the outline (headings) of the file
  ("M-s M-o" . consult-outline)
  ;; Search the current buffer
  ("M-s M-s" . consult-line)
  ;; yank
  ("M-s M-y" . consult-yank-pop)
  ;; yank from killring
  ("M-y" . consult-yank-from-kill-ring)
  ;; Switch to another buffer, or bookmarked file, or recently
  ;; opened file.
  ("C-x C-b" . consult-buffer)
  ("C-x b" . consult-buffer)
  ("C-x p b" . 'consult-project-buffer)
  )
  ;; Enable automatic preview at point in the *Completions* buffer. This is
  ;; relevant when you use the default completion UI.
  :hook (completion-list-mode . consult-preview-at-point-mode)

  ;; The :init configuration is always executed (Not lazy)
  :init

  ;; Optionally configure the register formatting. This improves the register
  ;; preview for `consult-register', `consult-register-load',
  ;; `consult-register-store' and the Emacs built-ins.
  (setq register-preview-delay 0.5
        register-preview-function #'consult-register-format)

  ;; Optionally tweak the register preview window.
  ;; This adds thin lines, sorting and hides the mode line of the window.
  (advice-add #'register-preview :override #'consult-register-window)

  ;; Use Consult to select xref locations with preview
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)

  ;; Configure other variables and modes in the :config section,
  ;; after lazily loading the package.
  ;:config
  ;; add buffer switcher source for perspective-el
  ;(consult-customize consult--source-buffer :hidden t :default nil)
  ;(add-to-list 'consult-buffer-sources persp-consult-source)
)

(rc/require 'marginalia)
(use-package marginalia
:defer .1
:config
(marginalia-mode)
)
(rc/require 'embark)
(use-package embark
:bind(("C-;" . 'embark-act))
)

;; wakatime
(rc/require 'wakatime-mode)
(use-package wakatime-mode
  :defer .1
  :config
  (global-wakatime-mode))

;; (rc/require 'good-scroll)
;; (use-package good-scroll
;;   :defer .1
;;   :if window-system          ; 在图形化界面时才使用这个插件
;;   :config (good-scroll-mode))
(straight-use-package '(ultra-scroll :host github :repo "jdtsmith/ultra-scroll"))
(use-package ultra-scroll
  :defer .1
  :init
  (setq scroll-conservatively 101 ; important!
        scroll-margin 0)
  :config
  (ultra-scroll-mode 1))

(rc/require 'ace-window)
(use-package ace-window
  :bind (("C-x o" . 'ace-window)))


;;; multiple cursors
(rc/require 'multiple-cursors)
(use-package multiple-cursors
  :bind (
  ("C-S-c C-S-c" . 'mc/edit-lines)
  ("C->".         'mc/mark-next-like-this)
("C-<".         'mc/mark-previous-like-this)
("C-c C-<".     'mc/mark-all-like-this)
("C-\"".        'mc/skip-to-next-like-this)
("C-:".         'mc/skip-to-previous-like-this)
)
)


(defun rc/duplicate-line ()
  "Duplicate current line"
  (interactive)
  (let ((column (- (point) (point-at-bol)))
        (line (let ((s (thing-at-point 'line t)))
                (if s (string-remove-suffix "\n" s) ""))))
    (move-end-of-line 1)
    (newline)
    (insert line)
    (move-beginning-of-line 1)
    (forward-char column)))

(global-set-key (kbd "C-,") 'rc/duplicate-line)

;; treesit-auto
;; (rc/require 'treesit-auto)
;; (use-package treesit-auto
;;   :defer .1
;;   :custom
;;   (treesit-auto-install 'prompt)
;;   :config
;;   (treesit-auto-add-to-auto-mode-alist nil)
;;   (global-treesit-auto-mode))

;; evil mode
(rc/require 'evil)
(use-package evil
  :defer .1 ;; don't block emacs when starting, load evil immediately after startup
  :init
  (setq evil-want-integration t) ;; required by evil-collection
  (setq evil-vsplit-window-right t) ;; like vim's 'splitright'
  (setq evil-split-window-below t) ;; like vim's 'splitbelow'
  :config
  (evil-mode)
  (evil-set-undo-system 'undo-tree)
  (add-to-list 'evil-emacs-state-modes 'eaf-mode)
  (add-to-list 'evil-emacs-state-modes 'eshell-mode)
  (add-to-list 'evil-emacs-state-modes 'shell-mode)
  ;; vim-like keybindings everywhere in emacs
;;   (use-package evil-collection
;;     :after evil
;;     :config
;;     (evil-collection-init '(eshell)))
)


;; undo
(rc/require 'undo-tree)
(use-package undo-tree
  :defer .1
  :config
  (global-undo-tree-mode 1)
  (setq undo-tree-auto-save-history nil))

;; rainbow-delimiters
(rc/require 'rainbow-delimiters)
(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode)
  :custom-face (font-lock-keyword-face ((t (:inherit font-lock-keyword-face :weight bold))))
)

;; magit
(rc/require 'magit)
(use-package magit
  :after prog-mode
  :config
  (setq magit-auto-revert-mode nil)
)

(rc/require 'git-gutter)
(use-package git-gutter
  :hook (prog-mode . git-gutter-mode)
)

(rc/require 'sideline-flycheck)
(use-package sideline-flycheck
  :hook (flycheck-mode . sideline-mode)
  :init
  (setq sideline-backends-right '(sideline-flycheck)))

(use-package sideline-flycheck :hook (flycheck-mode . sideline-flycheck-setup))

;; which-key
(rc/require 'which-key)
  (which-key-mode)

;;; dired
(require 'dired-x)
(use-package dired-x
  :after dired
  :config
  (define-key dired-mode-map (kbd "<left>") 'dired-up-directory)  ; 左键返回上级目录
  (define-key dired-mode-map (kbd "<right>") 'dired-find-file)     ; 右键进入文件夹或打开文件
  (setq dired-omit-files
        (concat dired-omit-files "\\|^\\..+$"))
  (setq-default dired-dwim-target t)
  (setq dired-listing-switches "-alh")
  (setq dired-mouse-drag-files t)
)

;; (rc/require 'dirvish)
;; (use-package dirvish
;;   :defer .1
;;   :config
;;   (setq dirvish-mode-line-format
;;         '(:left (sort symlink) :right (omit yank index)))
;;   (setq dirvish-mode-line-height 10)
;;   (setq dirvish-attributes
;;         '(nerd-icons file-time file-size collapse subtree-state vc-state git-msg))
;;   (setq dirvish-subtree-state-style 'nerd)
;;   (setq delete-by-moving-to-trash t)
;;   (setq dirvish-path-separators (list
;;                                  (format "  %s " (nerd-icons-codicon "nf-cod-home"))
;;                                  (format "  %s " (nerd-icons-codicon "nf-cod-root_folder"))
;;                                  (format " %s " (nerd-icons-faicon "nf-fa-angle_right"))))
;;   (setq dired-listing-switches
;;         "-l --almost-all --human-readable --group-directories-first --no-group")
;;   (dirvish-peek-mode) ; Preview files in minibuffer
;;   (dirvish-side-follow-mode) ; similar to `treemacs-follow-mode'
;;   (dirvish-override-dired-mode)
;; )

;; avy
(rc/require 'avy)
(use-package avy
  :bind (("C-c ;" . 'avy-goto-char)))

;; perspective-el -- multi project management
(rc/require 'perspective)
(use-package perspective
  :defer .1
  :hook (kill-emacs . persp-save-default)
  :bind (("C-x k" . persp-kill-buffer*)
         ("s-}" . persp-next)
         ("s-{" . persp-prev))
  :custom
  (persp-mode-prefix-key (kbd "C-c C-p"))
  :config
  (defun persp-save-default ()
    (let ((current-prefix-arg '(4)))
      (persp-state-save (concat user-emacs-directory "persp.el"))))
  (defun persp-create-aux ()
    "Create a new auxilliary perspective."
    (interactive)
    (let ((current-persp (persp-current-name)))
      (persp-switch "aux")
      (persp-switch current-persp)))
  (setq persp-state-default-file (concat user-emacs-directory "persp-"))
  (persp-mode))
(rc/require 'persp-projectile)
(use-package persp-projectile
  :after persp-mode
  :config
  (define-key projectile-mode-map (kbd "s-s") 'projectile-persp-switch-project)
)


;; eldoc
(rc/require 'eldoc-box)
(use-package eldoc-box
  :after eldoc
  :config
  (defun rex/eldoc-box-scroll-up ()
    "Scroll up in `eldoc-box--frame'"
    (interactive)
    (with-current-buffer eldoc-box--buffer
      (with-selected-frame eldoc-box--frame
        (scroll-down 3))))
  (defun rex/eldoc-box-scroll-down ()
    "Scroll down in `eldoc-box--frame'"
    (interactive)
    (with-current-buffer eldoc-box--buffer
      (with-selected-frame eldoc-box--frame
        (scroll-up 3))))
  (eldoc-box-hover-mode)
 )

;;; yasnippet
(rc/require 'yasnippet 'yasnippet-snippets)
(use-package yasnippet
  :hook
  (prog-mode . yas-minor-mode)
  :config
  ;(setq yas-snippet-dirs '("~/.emacs.snippets/")) ;; https://github.com/rexim/dotfiles/tree/master/.emacs.snippets
  (yas-reload-all)
  ;; unbind <TAB> completion
  (define-key yas-minor-mode-map [(tab)]        nil)
  (define-key yas-minor-mode-map (kbd "TAB")    nil)
  (define-key yas-minor-mode-map (kbd "<tab>")  nil)
  :bind
  (:map yas-minor-mode-map ("S-<tab>" . yas-expand)))

(use-package yasnippet-snippets
  :after yasnippet)

;; flycheck
(use-package flycheck
  :defer .1
  :config
  (setq flycheck-disabled-checkers '(python-flake8))
  (setq flycheck-check-syntax-automatically '(save mode-enabled))
)

(rc/require 'flycheck-pos-tip)
(use-package flycheck-pos-tip
  :after flycheck
  :config
  (flycheck-pos-tip-mode))

;; Projectile
(rc/require 'projectile)
(use-package projectile
  :bind (("C-c p" . projectile-command-map))
  :config
  (setq projectile-mode-line "Projectile")
  (setq projectile-switch-project-action 'projectile-dired)
  (setq projectile-track-known-projects-automatically nil))


(use-package pulse
  :custom-face
  (pulse-highlight-start-face ((t (:inherit region :background unspecified))))
  (pulse-highlight-face ((t (:inherit region :background unspecified :extend t))))
  :hook (((dumb-jump-after-jump imenu-after-jump) . +recenter-and-pulse)
         ((bookmark-after-jump magit-diff-visit-file next-error) . +recenter-and-pulse-line))
  :init
  (setq pulse-delay 0.1
        pulse-iterations 2)

  (defun +pulse-momentary-line (&rest _)
    "Pulse the current line."
    (pulse-momentary-highlight-one-line (point)))

  (defun +pulse-momentary (&rest _)
    "Pulse the region or the current line."
    (if (fboundp 'xref-pulse-momentarily)
        (xref-pulse-momentarily)
      (+pulse-momentary-line)))

  (defun +recenter-and-pulse(&rest _)
    "Recenter and pulse the region or the current line."
    (recenter)
    (+pulse-momentary))

  (defun +recenter-and-pulse-line (&rest _)
    "Recenter and pulse the current line."
    (recenter)
    (+pulse-momentary-line))

  (dolist (cmd '(recenter-top-bottom
                 other-window switch-to-buffer
                 aw-select toggle-window-split
                 windmove-do-window-select
                 pager-page-down pager-page-up
                 treemacs-select-window
                 tab-bar-select-tab))
    (advice-add cmd :after #'+pulse-momentary-line))

  (dolist (cmd '(pop-to-mark-command
                 pop-global-mark
                 goto-last-change))
    (advice-add cmd :after #'+recenter-and-pulse))

  (dolist (cmd '(symbol-overlay-basic-jump
                 compile-goto-error))
    (advice-add cmd :after #'+recenter-and-pulse-line))
  )

(defadvice! +auto-dim-other-buffers-auto-set-face (&rest _)
  :after #'enable-theme
  (set-face-background 'auto-dim-other-buffers-face (face-background 'mode-line)))
(rc/require 'auto-dim-other-buffers)
(use-package auto-dim-other-buffers
  :hook ((after-init . auto-dim-other-buffers-mode)
         (auto-dim-other-buffers-mode . +auto-dim-other-buffers-auto-set-face))
  :config
  (setq auto-dim-other-buffers-dim-on-focus-out nil
        auto-dim-other-buffers-dim-on-switch-to-minibuffer nil)
  )


;; Treemacs
(rc/require 'treemacs 'treemacs-projectile)
(use-package treemacs
  :config
  (setq treemacs-width 45
	treemacs-indentation 2
	treemacs-position 'left
	treemacs-icon-tag-leaf "0")
  (dolist (face '(treemacs-root-face
                  treemacs-git-unmodified-face
                  treemacs-git-modified-face
                  treemacs-git-renamed-face
                  treemacs-git-ignored-face
                  treemacs-git-untracked-face
                  treemacs-git-added-face
                  treemacs-git-conflict-face
                  treemacs-directory-face
                  treemacs-directory-collapsed-face
                  treemacs-file-face
                  treemacs-tags-face))
  (setq treemacs-git-mode nil)
  (set-face-attribute face nil :font (face-attribute 'default :font)))
  (define-key treemacs-mode-map [mouse-1] #'treemacs-single-click-expand-action)
    (treemacs-follow-mode t)
	(use-package treemacs-projectile
	  :after (treemacs projectile)
	  )
;;  	(use-package treemacs-evil
;;        :after (treemacs evil)
;;        )
  :bind (;("M-RET"       . treemacs-select-window)
          ("C-x t d"   . treemacs-select-directory)
         ("C-x t t"   . treemacs)))
(rc/require 'treemacs-projectile)
(use-package treemacs-projectile
  :after (treemacs projectile))
(rc/require 'treemacs-nerd-icons)
(use-package treemacs-nerd-icons
  :after treemacs
  :config
  (treemacs-load-theme "nerd-icons"))

(rc/require 'dogears)
(use-package dogears
  ;; These bindings are optional, of course:
  :bind (:map global-map
              ("M-g d" . dogears-go)
              ("M-g M-b" . dogears-back)
              ("M-g M-f" . dogears-forward)
              ("M-g M-d" . dogears-list)
              ("M-g M-D" . dogears-sidebar)))

;;; terminal
(defun my-switch-to-comint-cmd-buffer ()
  "Create a comint buffer for cmd and switch to it."
  (interactive)
  (let ((buffer (make-comint-in-buffer "cmd" nil "cmd" nil)))
    (with-current-buffer buffer
      (set-buffer-process-coding-system 'gbk 'gbk))
    (switch-to-buffer buffer)))

(global-set-key (kbd "C-c c") 'my-switch-to-comint-cmd-buffer)


(provide 'init-packages)
